home *** CD-ROM | disk | FTP | other *** search
- // ScreenSaver.m
- //
- // This class is the brains behind the SpaceSaver login bundle.
- //
- // This recast of BackSpace into a ScreenSaver bundle for use when no one
- // is logged in was done by Christopher_Lane@Med.Stanford.EDU, December 1993.
- //
- // The original BackSpace application was done by Sam Streeper of NeXT,
- // with contributions from Bill Bumgarner, Lennart Lovstrand, Bruce Blumberg,
- // shou-h@nexus.or.jp and others. Many of the code comments are also Sam's.
- //
- // Undocumented 3.3 SpaceSaver.loginbundle details obtained via /bin/otool.
- //
- // You may freely copy, distribute, and reuse the code in this example.
- // NeXT disclaims any warranty of any kind, expressed or implied, as
- // to its fitness for any particular use.
-
- #import "SpaceSaver.h"
- #import "SpaceView.h"
- #import "psfuncts.h"
-
- #import <sys/dir.h>
- #import <appkit/appkit.h>
-
- #ifdef KLUDGE
- static NXDefaultsVector MovieShowDefaults = { // keep uninitialized MovieShowView from failing
- {"Jump", "NO"},
- {"SlideFrames", "NO"},
- {"SlidePauses", "YES"},
- {NULL}
- };
-
- static NXDefaultsVector MultiViewDefaults = { // keep uninitialized MulitView from failing
- {"MultiLayout", "0"},
- {"MultiViews", "Space"},
- {NULL}
- };
- #endif
-
- static NXDefaultsVector BackSpaceDefaults = {
- {"ViewDirectory", "/LocalLibrary/BackSpaceViews"},
- {"viewType", "All"},
- {"priority", "4"},
- {NULL}
- };
-
- static char *compiledViewNames[] = { "Space" }, *applicationName, *launchDir;
-
- #define COMVIEWCOUNT (sizeof(compiledViewNames) / sizeof(*compiledViewNames))
-
- typedef enum {MINIMUMPRIORITY = 0, BACKSPACEPRIORITY = 4, DEFAULTPRIORITY = 16, MAXIMUMPRIORITY = 31} PRIORITY;
-
- #define streq(s, t) (strcmp(s, t) == 0)
-
- #define MSECPERSEC (1000)
- #define USECPERMSEC (1000)
-
- static id _BSThinker, appDelegate;
- id BSThinker() { return _BSThinker; }
-
- static int eventMask, basePriority;
- BOOL doesDidLockFocus;
-
- #define BACKSPACENAME "BackSpace"
-
- #define DEFAULTIMAGENAME "defaultImage"
-
- #define BINARYEXTENSION "BackO"
- #define BUNDLEEXTENSION "BackModule"
-
- @implementation SpaceSaver
-
- + alloc
- {
- static id instance; // probably not a good idea to have more than one of these...
-
- if (instance == nil) instance = [super alloc];
-
- return (self = instance);
- }
-
- - didStartScreenSaver
- {
- if (screenSaverVal) return self;
-
- if (!doingSaver) [self createScreenSaver];
-
- [self setScreenSaver:YES];
- #ifndef DEBUG
- PShidecursor();
- [self blackOutAllScreens];
- #endif
- [self setVirtualViewIndex];
-
- [[spaceView fillBoundsWithBlack] display];
- [[spaceWindow display] makeKeyAndOrderFront:self];
-
- #ifndef DEBUG
- (void) [spaceWindow addToEventMask:NX_MOUSEMOVEDMASK];
- #endif
- eventMask = [spaceWindow eventMask];
-
- NXPing();
-
- if (priority != basePriority) cthread_priority(cthread_self(), priority, FALSE);
-
- if ([spaceView respondsTo:@selector(enteredScreenSaverMode)]) [spaceView enteredScreenSaverMode];
-
- return self;
- }
-
- - didStopScreenSaver
- {
- if (!screenSaverVal) return self;
-
- [self setScreenSaver:NO];
-
- if ([spaceView respondsTo:@selector(willExitScreenSaverMode)]) [spaceView willExitScreenSaverMode];
-
- if (basePriority != priority) cthread_priority(cthread_self(), basePriority, FALSE);
-
- [spaceWindow orderOut:self];
-
- (void) [spaceWindow setEventMask:eventMask]; // loaded view may have changed event mask on us.
- #ifndef DEBUG
- (void) [spaceWindow removeFromEventMask:NX_MOUSEMOVEDMASK];
- [self unBlackOutAllScreens];
- PSshowcursor();
- #endif
- return self;
- }
-
- - oneStep
- {
- // NXEvent dummyEvent;
-
- if (!screenSaverVal) return nil; // timed entry misfire
-
- [spaceView lockFocus]; {
- if (doesDidLockFocus) [spaceView didLockFocus];
- // do {
- [spaceView oneStep];
- [spaceWindow flushWindow];
- NXPing(); // Synchronize postscript for smoother animation
- // } while (screenSaverVal && ([NXApp peekNextEvent:NX_ALLEVENTS into:&dummyEvent waitFor:0 threshold:NX_BASETHRESHOLD] == NULL));
- } [spaceView unlockFocus];
-
- return self;
- }
-
- - createScreenSaver
- {
- const char *string;
- char buffer[MAXPATHLEN];
-
- NXRegisterDefaults(BACKSPACENAME, BackSpaceDefaults);
- #ifdef KLUDGE
- (void) sprintf(buffer, "%s/defaultImage.tiff", [[NXBundle bundleForClass:[self class]] directory]);
- (void) NXSetDefault(BACKSPACENAME, "imageFile", buffer);
- [self borrowDefaults:BACKSPACENAME]; // if module does NXGetDefaultValue([NXApp appName], ...
- NXRegisterDefaults(applicationName, MultiViewDefaults);
- #endif
- launchDir = NXCopyStringBuffer([[NXBundle mainBundle] directory]);
-
- [NXApp getScreens:&screens count:&screenCount];
-
- [self getViewType];
-
- [self getPrioritySetting];
-
- [self getImageFile];
-
- if ((string = NXGetDefaultValue(BACKSPACENAME, "viewType")) != NULL) realViewIndex = [moduleList indexOfName:string];
-
- doingSaver = YES;
-
- return self;
- }
-
- - init // do the easy stuff, leave the rest for first time we 'start'
- {
-
- _BSThinker = self;
-
- screenSaverVal = doingSaver = NO;
-
- backZone = NXCreateZone(vm_page_size, vm_page_size, YES);
-
- moduleList = [[ModuleList alloc] init];
-
- basePriority = priority = DEFAULTPRIORITY;
-
- realViewIndex = virtualViewIndex = -1;
-
- currentInspector = commonImageInspector = nullInspector = nil;
-
- appDelegate = [NXApp delegate];
-
- applicationName = (char *) [NXApp appName];
- #ifndef DEBUG
- srandom(time(NULL));
- #endif
- #ifdef KLUDGE
- (void) fmod(2.0, 3.0); // force 'fmod' to not inline and let SpewView load
- #endif
- return self;
- }
-
- - free
- {
- [self didStopScreenSaver];
-
- return [super free];
- }
-
- int readstr(FILE *stream, char *s) // wish I could use scanf but %s won't read unanticipated "xxx yyy"
- {
- int c, flag = FALSE;
-
- while ((c = getc(stream)) != EOF) {
- if (c == '"') flag = !flag;
- else if ((c == ' ' || c == '\n') && !flag) break;
- *s++ = c;
- }
- *s = '\0';
-
- return c;
- }
-
- - borrowDefaults:(const char *) realOwner
- { // NeXT should have defined (NXDefaultsVector *) NXReadDefaults(const char *owner)
- FILE *pipe;
- char string[MAXPATHLEN];
-
- (void) sprintf(string, "dread -o %s", realOwner);
-
- if ((pipe = popen(string, "r")) != NULL) {
- char owner[MAXNAMLEN], name[MAXNAMLEN], value[MAXPATHLEN];
-
- while (readstr(pipe, owner) != EOF && readstr(pipe, name) != EOF && readstr(pipe, value) != EOF) {
- if (streq(owner, realOwner) && NXGetDefaultValue(applicationName, name) == NULL) {
- (void) NXSetDefault(applicationName, name, value); // loginwindow & BackSpace overlap!
- }
- #ifdef DEBUG
- else (void) fprintf(stderr, "%s %s %s\n", owner, name, value);
- #endif
- }
- (void) pclose(pipe);
- }
- else return nil;
-
- return self;
- }
-
- - (NXZone *) backZone { return backZone; }
-
- - (ModuleList *) moduleList { return moduleList; }
-
- - installSpaceViewIntoWindow
- {
- NXRect cvrect;
- unsigned int i, count;
- id subviews, contentView = [spaceWindow contentView];
-
- [contentView getBounds:&cvrect];
-
- subviews = [contentView subviews]; // remove old subviews, this is overkill really...
- for (i = 0, count = [subviews count]; i < count; i++) [[subviews objectAt:i] removeFromSuperview];
-
- (void) [contentView addSubview:spaceView];
-
- [contentView setAutoresizeSubviews:YES]; // don't really need to resize but some views break if you don't!
-
- [spaceView setAutosizing:NX_WIDTHSIZABLE | NX_HEIGHTSIZABLE];
-
- [spaceView sizeTo:cvrect.size.width :cvrect.size.height];
-
- return self;
- }
-
- - (int) backingTypeForView:aView
- {
- if ([aView respondsTo:@selector(useBufferedWindow)] && [aView useBufferedWindow]) return NX_BUFFERED;
-
- return NX_RETAINED;
- }
-
- - createBigWindowIfNecessaryForBacking:(int) backing
- {
- id window = nil;
- #ifndef DEBUG
- const NXScreen *mainScreen = [NXApp mainScreen];
- NXRect r = mainScreen->screenBounds;
- #else
- NXRect r = {{0, 0}, {640, 480}};
- #endif
- if ((backing == NX_RETAINED) && !bigUnbufferedWindow) {
- window = bigUnbufferedWindow = [[Window allocFromZone:backZone]
- initContent:&r style:NX_TOKENSTYLE backing:NX_NONRETAINED buttonMask:0 defer:NO];
- }
-
- if ((backing == NX_BUFFERED) && !bigBufferedWindow) {
- window = bigBufferedWindow = [[Window allocFromZone:backZone]
- initContent:&r style:NX_TOKENSTYLE backing:NX_BUFFERED buttonMask:0 defer:NO];
- [[bigBufferedWindow setOneShot:YES] setDynamicDepthLimit:YES]; // want window depth to match device!
- }
-
- if (window != nil) {
- [[window useOptimizedDrawing:YES] setBackgroundGray:NX_BLACK];
- tweakWindow([window windowNum], SAVERTIER);
- }
-
- return self;
- }
-
- - setScreenSaver:(BOOL) val
- {
- screenSaverVal = val;
-
- // I don't handle any app* messages but some modules assume I do,
-
- if (screenSaverVal) [NXApp setDelegate:self];
- else [NXApp setDelegate:appDelegate];
-
- return self;
- }
-
- - getPrioritySetting
- {
- struct thread_basic_info info;
- int value, count = THREAD_BASIC_INFO_COUNT;
- const char *string = NXGetDefaultValue(BACKSPACENAME, "priority");
-
- if(thread_info(cthread_thread(cthread_self()), THREAD_BASIC_INFO, (thread_info_t) &info, &count) == KERN_SUCCESS) {
- basePriority = info.base_priority;
- }
-
- if (sscanf(string, "%d", &value) == 1 && value >= MINIMUMPRIORITY && value <= MAXIMUMPRIORITY) priority = value;
- else priority = BACKSPACEPRIORITY;
-
- return self;
- }
-
- BStimeval currentTimeInMs()
- {
- struct timeval curTime;
-
- gettimeofday(&curTime, NULL);
-
- return ((curTime.tv_sec * MSECPERSEC) + (curTime.tv_usec / USECPERMSEC));
- }
-
- // Additional methods to handle a common image object for views.
- // Lennart Lovstrad, Rank Xerox EuroPARC, August 1991.
-
- - setImageFromName:(const char *) name
- {
- if (image != nil) [image free];
-
- image = [[NXImage allocFromZone:backZone] initFromSection:name];
-
- return [self commonImageInit];
- }
-
- - setImageFromFile:(const char *) filename
- {
- if (image != nil) [image free];
-
- image = [[NXImage allocFromZone:backZone] initFromFile:filename];
-
- return [self commonImageInit];
- }
-
- - commonImageInit
- {
- if ([spaceView respondsTo:@selector(setImage:)]) [spaceView setImage:image];
-
- if ([self backingTypeForView:spaceView] != NX_BUFFERED) [[spaceView fillBoundsWithBlack] display];
-
- return self;
- }
-
- - getImageFile
- {
- const char *filename;
-
- if ((filename = NXGetDefaultValue(BACKSPACENAME, "imageFile")) != NULL) [self setImageFromFile:filename];
- else [self setImageFromName:DEFAULTIMAGENAME];
-
- return self;
- }
-
- float frandom() // This should return a float between 0 and 1
- {
- return (((float) (random() & MAXLONG)) / (float) MAXLONG);
- }
-
- float randBetween(float low, float high)
- {
- float temp = low;
-
- if (low > high) { low = high; high = temp; }
-
- return (((high - low) * frandom()) + low);
- }
-
- // float randBetween(float a, float b) { return (MIN(a, b) + (fabs(a - b) * frandom())); }
-
- - getViewType // must invoke this before creating window or setting up the views
- {
- unsigned int i;
-
- [self loadViewsFrom:NXGetDefaultValue(BACKSPACENAME, "ViewDirectory")];
-
- for (i = 0; i < COMVIEWCOUNT; i++) {
- [moduleList addObject:[[ModuleInfo alloc] initWithView:nil name:compiledViewNames[i] path:NULL]];
- }
-
- [moduleList sort];
-
- return self;
- }
-
- // this method is the actual view setting mechanism,
- // guaranteed to get called to set the view
-
- - setVirtualViewIndex
- {
- if (realViewIndex != -1) virtualViewIndex = realViewIndex;
- else {
- #ifndef DEBUG
- virtualViewIndex = random() % [moduleList count];
- #else
- if (++virtualViewIndex >= [moduleList count]) virtualViewIndex = 0;
- #endif
- }
-
- while ([self selectScreenSaverViews] == nil || ([spaceView respondsTo:@selector(isBoringScreenSaver)] && [spaceView isBoringScreenSaver])) {
- if (++virtualViewIndex >= [moduleList count]) virtualViewIndex = 0;
- }
-
- return self;
- }
-
- - selectScreenSaverViews
- {
- int myBacking;
-
- if ((spaceView = [self backView]) == nil) return nil;
-
- myBacking = [self backingTypeForView:spaceView];
-
- [self createBigWindowIfNecessaryForBacking:myBacking];
-
- spaceWindow = (myBacking == NX_BUFFERED) ? bigBufferedWindow : bigUnbufferedWindow;
- #ifdef DEBUG
- (void) fprintf(stderr, "Window type = NX_%sBUFFERED\n", (myBacking == NX_BUFFERED) ? "" : "UN");
- #endif
- [self installSpaceViewIntoWindow];
- #ifdef KLUDGE
- if ([spaceView respondsTo:@selector(inspector:)]) currentInspector = [spaceView inspector:self];
- else currentInspector = [self nullInspector]; // don't use inspectors, but some modules use to initialize -- sigh
- #endif
- if ([spaceView respondsTo:@selector(setImage:)]) [spaceView setImage:image];
- if ([spaceView respondsTo:@selector(newWindow)]) [spaceView newWindow];
- doesDidLockFocus = [spaceView respondsTo:@selector(didLockFocus)];
-
- return self;
- }
-
- - backView
- {
- id theView;
- ModuleInfo *info = [moduleList objectAt:virtualViewIndex];
-
- if ((theView = [info view]) == nil) {
- char class[MAXNAMLEN];
- #ifdef DEBUG
- NXRect aFrame = {{0, 0}, {640, 480}};
- #else
- const NXScreen *mainScreen = [NXApp mainScreen];
- NXRect aFrame = mainScreen->screenBounds;
- #endif
- (void) sprintf(class, "%sView", [info viewName]);
-
- // before I loaded all classes at launch time; now classes are loaded only as
- // needed. This idea and some of the code here is from bill bumgarner, thanx!
-
- if ([info path]) { // we have path but no instance, must load class
- char path[MAXPATHLEN]; char *filenames[] = {path, NULL}; // order dependency
- long result;
- struct mach_header *header;
- NXStream *stream = NULL;
- #ifdef DEBUG
- char *address;
- int length, maximum;
- #endif
- do {
- #ifdef DEBUG
- stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
- #endif
- (void) sprintf(path, "%s/%s.%s", [info path], class, BINARYEXTENSION);
- result = objc_loadModules(filenames, stream, NULL, &header, NULL);
- #ifdef DEBUG
- NXFlush(stream);
- NXGetMemoryBuffer(stream, &address, &length, &maximum);
- (void) fprintf(stderr, address);
- NXCloseMemory(stream, NX_FREEBUFFER);
- #endif
- // objc_loadModules succeeds with a warning if the architecture of the
- // object file is wrong, so we better check if we really got a class
-
- if (result == 0 && objc_getClass(class) == nil) result = -1;
-
- } while (result != 0 && [info useNextPath] != nil);
-
- [info discardAltPaths];
-
- #ifdef DEBUG
- (void) fprintf(stderr, "Dynamic load of class: %s -- %s!\n", class, (result == 0) ? "succeeded" : "failed");
- #endif
- if (result != 0) return nil; // Ugh, failed -- return failure.
- else [info setHeader:header];
- }
-
- theView = [objc_getClass(class) allocFromZone:backZone]; // at this point we must have a valid name for a loaded class
- #ifdef KLUDGE
- if (streq(class, "MovieShowView")) { // don't prompt user for move name
- char buffer[MAXPATHLEN];
-
- NXRegisterDefaults("MovieShow", MovieShowDefaults);
-
- (void) sprintf(buffer, "%s/defaultImage.anim", [[NXBundle bundleForClass:[self class]] directory]);
- (void) NXSetDefault("MovieShow", "Movie", buffer);
- }
- #endif
- [info setView:[theView initFrame:&aFrame]];
- }
-
- return theView;
- }
-
- // Dynamically load all object files found in the specified directory
- // if we find a module in several places, we save the additional paths
- // in case they point to modules for different architectures
-
- - loadViewsFrom: (const char *) dirname
- {
- DIR *dir;
- int index;
- struct direct *de;
- BOOL validName, filePackage;
- char *iptr, name[MAXNAMLEN], path[MAXPATHLEN];
-
- if ((dir = opendir(dirname)) == NULL) return nil;
-
- while ((de = readdir(dir)) != NULL) {
- if (de->d_name[0] == '.') continue; // Ignore '.'-files (not really necessary, I guess)
-
- filePackage = validName = NO;
-
- if (de->d_namlen > (strlen(BINARYEXTENSION) + 1) && streq(&de->d_name[de->d_namlen - (strlen(BINARYEXTENSION) + 1)], "." BINARYEXTENSION))
- validName = YES;
- else if (de->d_namlen > (strlen(BUNDLEEXTENSION) + 1) && streq(&de->d_name[de->d_namlen - (strlen(BUNDLEEXTENSION) + 1)], "." BUNDLEEXTENSION))
- validName = filePackage = YES;
-
- if (!validName) continue;
-
- if (filePackage) (void) sprintf(path, "%s/%s", dirname, de->d_name);
- else (void) strcpy(path, dirname);
-
- if ((iptr = rindex(strcpy(name, de->d_name), 'V')) != NULL) *iptr = '\0'; // Smash out the 'V' in "FooView.BackO"
-
- if ((index = [moduleList indexOfName:name]) != -1) {
- [[moduleList objectAt:index] appendPath:path];
- continue;
- }
-
- // I used to load the class at this time; this got horribly inefficient.
- // I now wait until I'm about to instantiate a view before doing this (thanx bbum!)
-
- [moduleList addObject:[[ModuleInfo alloc] initWithView:NULL name:name path:path]];
- }
-
- closedir(dir);
-
- return self;
- }
-
- - (const char *) appDirectory { return launchDir; }
-
- - (const char *) moduleDirectory:(const char *) name
- {
- int index = [moduleList indexOfName:name];
-
- if (index == -1) return NULL;
-
- return [[moduleList objectAt:index] path];
- }
-
- - (struct mach_header *) headerForModule:(const char *) name
- {
- int index = [moduleList indexOfName:name];
-
- if (index == -1) return NULL;
-
- return [[moduleList objectAt:index] header];
- }
-
- // In the multi-headed case, I gotta throw a black window over all
- // the screens so they don't burn in while I do animation on one.
- // You'd want to black out all screen in every case if you switched
- // animations on the fly to prevent the screen from possibly being
- // unlocked for a moment.
-
- // Hmm, I don't know why I didn't just put a single big non retained
- // window over all screens instead...
-
- - blackOutAllScreens
- {
- id window;
- NXRect *rect;
- unsigned int i;
-
- if (screenCount <= 1) return self;
-
- if (screenList == nil) screenList = [[List alloc] initCount:screenCount];
-
- for (i = 0; i < screenCount; i++) {
- rect = &screens[i].screenBounds;
-
- window = [[Window allocFromZone:backZone] initContent:rect style:NX_TOKENSTYLE backing:NX_NONRETAINED buttonMask:0 defer:NO];
-
- [screenList addObject:window];
-
- [window setBackgroundGray:NX_BLACK];
- (void) [window addToEventMask:NX_MOUSEMOVEDMASK];
- tweakWindow([window windowNum], SAVERTIER - 1);
- [[window placeWindowAndDisplay:rect] orderFront:self];
- }
-
- return self;
- }
-
- - unBlackOutAllScreens
- {
- if (screenCount <= 1) return self;
-
- [[screenList makeObjectsPerform:@selector(orderOut:) with:self] freeObjects];
-
- return self;
- }
-
- - nullInspector { return nullInspector; }
-
- - commonImageInspector { return commonImageInspector; }
-
- @end
-